/*!
 * @file        bsp_InternalFlash.c
 *
 * @brief       Read and write internal flash for usb device storage disk
 *
 * @version     V1.0.0
 *
 * @date        2022-05-25
 *
 */

#include "bsp_InternalFlash.h"
#include "usbd_msc_scsi.h"

#if !(TEST_USB_SPEED)
uint32_t ramBuf[FLASH_BLK_SIZ/4];
#else
uint32_t ramBuf[(REAL_STORAGE_BLK_NBR*STORAGE_BLK_SIZ)/4];
#endif

/*!
 * @brief  Write data to the FLASH
 *
 * @param  buf: Pointer to the buffer to write data to the FLASH
 *
 * @param  writeAddr: FLASH's offset address to write to
 *
 * @param  writeLen: number of bytes to write to the FLASH
 *
 * @retval SCSI_OK or SCSI_FAILL
 */
uint8_t Flash_Write(uint32_t *wbuf, uint32_t writeAddr, uint16_t writeLen)
{
    uint32_t n,index,flagErase = 0;
    uint32_t startAddress = 0;
    FMC_SECTOR_T FLASH_SECTOR = FLASH_BLOCK1_SECTOR;

    /** Determine the corresponding physical address according to the writeAddr */
    switch (writeAddr/(FLASH_BLK_SIZ))
    {
        case 0:
            startAddress = FLASH_BLOCK0_START_ADDR;
            FLASH_SECTOR = FLASH_BLOCK0_SECTOR;
            break;
        case 1:
            startAddress = FLASH_BLOCK1_START_ADDR;
            FLASH_SECTOR = FLASH_BLOCK1_SECTOR;
            break;
        case 2:
            startAddress = FLASH_BLOCK2_START_ADDR;
            FLASH_SECTOR = FLASH_BLOCK2_SECTOR;
            break;
        case 3:
            startAddress = FLASH_BLOCK3_START_ADDR;
            FLASH_SECTOR = FLASH_BLOCK3_SECTOR;
            break;
        case 4:
            startAddress = FLASH_BLOCK4_START_ADDR;
            FLASH_SECTOR = FLASH_BLOCK4_SECTOR;
            break;
        case 5:
            startAddress = FLASH_BLOCK5_START_ADDR;
            FLASH_SECTOR = FLASH_BLOCK5_SECTOR;
            break;
        case 6:
            startAddress = FLASH_BLOCK6_START_ADDR;
            FLASH_SECTOR = FLASH_BLOCK6_SECTOR;
            break;
        case 7:
            startAddress = FLASH_BLOCK7_START_ADDR;
            FLASH_SECTOR = FLASH_BLOCK7_SECTOR;
            break;
        case 8:
            startAddress = FLASH_BLOCK8_START_ADDR;
            FLASH_SECTOR = FLASH_BLOCK8_SECTOR;
            break;
        case 9:
            startAddress = FLASH_BLOCK9_START_ADDR;
            FLASH_SECTOR = FLASH_BLOCK9_SECTOR;
            break;
        default:
            break;
    }
    writeAddr = writeAddr%(FLASH_BLK_SIZ);


    /** Determine whether sectors need to be erased */
    index = 0;
    flagErase = 0;
    for(n = 0; n < writeLen/4; n++)
    {
        ramBuf[index] = (*(__IO uint32_t*)(startAddress + writeAddr + n*4));
        if(ramBuf[index] != 0xffffffff)
        {
            flagErase = 1;
            break;
        }
        index++;
    }

    FMC_Unlock();

    if(flagErase == 1)
    {
        /** read original data */
        for(n = 0; n < FLASH_BLK_SIZ/4; n++)
        {
            ramBuf[n] = (*(__IO uint32_t*)(startAddress + n*4));
        }

        /** Erase Sector */
        FMC_EraseSector(FLASH_SECTOR, FMC_VOLTAGE_3);

        /** Modify Data */
        for(n = 0; n < writeLen/4; n++)
        {
            ramBuf[(writeAddr/4+ n)] = wbuf[n];
        }

        /** write back */
        for(n = 0; n < FLASH_BLK_SIZ/4; n++)
        {
            FMC_ProgramWord(startAddress + n*4, ramBuf[n]);
        }
    }
    else
    {
        /**  write to the FLASH */
        for(n = 0; n < writeLen/4; n++)
        {
            FMC_ProgramWord((startAddress + writeAddr + n*4), wbuf[n]);
        }
    }
    FMC_Lock();

    return SCSI_OK;
}

/*!
 * @brief  Read data to the FLASH
 *
 * @param  buf: Pointer to the buffer to read data from the FLASH
 *
 * @param  readAddr: FLASH's offset address to read from
 *
 * @param  readLen: number of bytes to read from the FLASH
 *
 * @retval SCSI_OK or SCSI_FAILL
 */
uint8_t Flash_Read(uint32_t *rbuf, uint32_t readAddr, uint16_t readLen)
{
    uint32_t n;
    uint32_t startAddress = 0;

    /** Determine the corresponding physical address according to the readAddr */
    switch (readAddr/(FLASH_BLK_SIZ))
    {
        case 0:
            startAddress = FLASH_BLOCK0_START_ADDR;
            break;
        case 1:
            startAddress = FLASH_BLOCK1_START_ADDR;
            break;
        case 2:
            startAddress = FLASH_BLOCK2_START_ADDR;
            break;
        case 3:
            startAddress = FLASH_BLOCK3_START_ADDR;
            break;
        case 4:
            startAddress = FLASH_BLOCK4_START_ADDR;
            break;
        case 5:
            startAddress = FLASH_BLOCK5_START_ADDR;
            break;
        case 6:
            startAddress = FLASH_BLOCK6_START_ADDR;
            break;
        case 7:
            startAddress = FLASH_BLOCK7_START_ADDR;
            break;
        case 8:
            startAddress = FLASH_BLOCK8_START_ADDR;
            break;
        case 9:
            startAddress = FLASH_BLOCK9_START_ADDR;
            break;
        default:
            break;
    }
    readAddr = readAddr%(FLASH_BLK_SIZ);

    /** read data from the FLASH */
    for(n = 0; n < readLen/4; n++)
    {
        rbuf[n] = (*(__IO uint32_t*)(startAddress + readAddr + n*4));
    }

    return SCSI_OK;
}






